﻿using System;
using System.Xml;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Json;
using System.Security;
using System.Security.Permissions;
using System.IO;
using System.Globalization;
using System.Reflection;


namespace LongNet.Util
{
    /// <summary>
    /// A simple serializer that provides static methods to serialize or deserialize data.
    /// This class cannot be inherited.
    /// </summary>
    public static class Serializer
    {
        #region Fields

        /// <summary>
        /// Readonly value indicating whether if binary serialization is allowed.
        /// </summary>
        public static readonly bool CanBinarySerialize;

        private static Regex toDateRegex, fromDateRegex;
        private static MatchEvaluator toDateEvaluator, fromDateEvaluator;
        private static Dictionary<string, XmlSerializer> xmlSerializers = new Dictionary<string, XmlSerializer>();
        private static Dictionary<int, List<Type>> jsonKnownTypes = new Dictionary<int, List<Type>>();
        private static Dictionary<string, DataContractJsonSerializer> jsonSerializers = new Dictionary<string, DataContractJsonSerializer>();

        #endregion

        #region Constructors

        static Serializer()
        {
            try
            {
                (new SecurityPermission(SecurityPermissionFlag.SerializationFormatter)).Demand();
                CanBinarySerialize = true;
            }
            catch (SecurityException)
            {
                CanBinarySerialize = false;
            }

            // 将时间"\/Date(10000000000-0700)\/" 格式转换成 "yyyy-MM-dd HH:mm:ss" 格式的字符串
            toDateRegex = new Regex(@"\\/Date\((\d+)(\-|\+)\d+\)\\/");
            toDateEvaluator = new MatchEvaluator((Match m) =>
            {
                try
                {
                    DateTime dt = new DateTime(1970, 1, 1);
                    dt = dt.AddMilliseconds(long.Parse(m.Groups[1].Value));
                    dt = dt.ToLocalTime();
                    return dt.ToString("yyyy-MM-dd HH:mm:ss");
                }
                catch
                {
                    return string.Empty;
                }
            });

            // 将时间 "yyyy-MM-dd HH:mm:ss" 格式转换成"\/Date(10000000000-0700)\/" 格式的字符串
            fromDateRegex = new Regex(@"(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})");
            fromDateEvaluator = new MatchEvaluator((Match m) =>
            {
                try
                {
                    DateTime dt = DateTime.Parse(m.Groups[1].Value);
                    dt = dt.ToUniversalTime();
                    TimeSpan ts = dt - Constants.GmtDateTime;
                    return string.Format("\\/Date({0}-0700)\\/", ts.TotalMilliseconds);
                }
                catch
                {
                    return string.Empty;
                }
            });
        }

        #endregion

        #region Methods

        #region Json Serialization

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static List<Type> GetSerializeKnownTypes(object obj)
        {
            obj.ThrowsIfNull("obj");

            List<Type> list;

            int hash = obj.GetType().GetHashCode();

            if (jsonKnownTypes.TryGetValue(hash, out list) == false)
            {
                list = new List<Type>();
                list.Add(obj.GetType());
                GetPropertyTypes(obj, ref list);

                jsonKnownTypes[hash] = list;
            }

            return list;
        }

        private static void GetPropertyTypes(object obj, ref List<Type> types)
        {
            var properties = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            if (properties != null && properties.Length > 0)
            {
                foreach (var p in properties)
                {
                    object value;
                    try
                    {
                        value = p.GetValue(obj, null);
                    }
                    catch
                    {
                        continue;
                    }

                    if (value == null)
                        return;

                    if (types.Contains(value.GetType()))
                        continue;

                    types.Add(value.GetType());
                    GetPropertyTypes(value, ref types);
                }
            }
        }

        /// <summary>
        /// Gets the cached <see cref="T:DataContractJsonSerializer"/>.
        /// </summary>
        /// <param name="type">A .NET object type.</param>
        /// <returns>An instance of <see cref="T:DataContractJsonSerializer"/></returns>
        public static DataContractJsonSerializer GetJsonSerializer(Type type)
        {
            return GetJsonSerializer(type, Type.EmptyTypes);
        }

        /// <summary>
        /// Gets the cached <see cref="T:DataContractJsonSerializer"/>.
        /// </summary>
        /// <param name="type">A .NET object type.</param>
        /// <param name="knownTypes">
        /// An <see cref="T:IEnumerable`1"/> of <see cref="T:System.Type"/> that contains 
        /// the types that may be present in the object graph.
        /// </param>
        /// <returns>An instance of <see cref="T:DataContractJsonSerializer"/></returns>
        public static DataContractJsonSerializer GetJsonSerializer(Type type, IEnumerable<Type> knownTypes)
        {
            var key = new StringBuilder();
            if (knownTypes != null)
            {
                foreach (var t in knownTypes)
                {
                    key.AppendFormat("_{0}", t.GetHashCode());
                }
            }

            string hash = string.Format("{0}{1}", type.GetHashCode(), key);

            DataContractJsonSerializer serializer = null;
            if (jsonSerializers.TryGetValue(hash, out serializer))
            {
                return serializer;
            }
            else
            {
                jsonSerializers[hash] = new DataContractJsonSerializer(type, knownTypes ?? Type.EmptyTypes);

                return jsonSerializers[hash];
            }
        }

        /// <summary>
        /// Converts a .NET object to a Json string.
        /// </summary>
        /// <param name="obj">Object to convert.</param>
        /// <returns>A json string represting the object parameter. The returned value will be null of the object is null.</returns>
        public static string ConvertToJsonString(object obj)
        {
            return ConvertToJsonString(obj, Type.EmptyTypes);
        }

        /// <summary>
        /// Converts a .NET object to a string of Json.
        /// </summary>
        /// <param name="obj">Object to convert.</param>
        /// <param name="knownTypes">
        /// An <see cref="T:IEnumerable`1"/> of <see cref="T:System.Type"/> that contains 
        /// the types that may be present in the object graph.
        /// </param>
        /// <returns>A json string represting the object parameter. The returned value will be null of the object is null.</returns>
        public static string ConvertToJsonString(object obj, IEnumerable<Type> knownTypes)
        {
            string json = null;

            if (obj != null)
            {
                using (var ms = new MemoryStream())
                {
                    var ser = GetJsonSerializer(obj.GetType(), knownTypes);
                    ser.WriteObject(ms, obj);
                    json = Encoding.UTF8.GetString(ms.ToArray());
                }

                json = toDateRegex.Replace(json, toDateEvaluator);
            }

            return json;
        }

        /// <summary>
        /// Converts a string of Json to the supplied object type. 
        /// You will need to cast this object back to its expected type. 
        /// </summary>
        /// <param name="json">Json representing a .NET object.</param>
        /// <param name="type">The type of object which the Json represents.</param>
        /// <returns>An instance of object or null if the value of json is null or empty.</returns>
        public static object ConvertFromJsonString(string json, Type type)
        {
            return ConvertFromJsonString(json, type, Type.EmptyTypes);
        }

        /// <summary>
        /// Converts a string of Json to the supplied object type. 
        /// You will need to cast this object back to its expected type. 
        /// </summary>
        /// <param name="json">Json representing a .NET object.</param>
        /// <param name="type">The type of object which the Json represents.</param>
        /// <param name="knownTypes">
        /// An <see cref="T:IEnumerable`1"/> of <see cref="T:System.Type"/> that contains 
        /// the types that may be present in the object graph.
        /// </param>
        /// <returns>An instance of object or null if the value of json is null or empty.</returns>
        public static object ConvertFromJsonString(string json, Type type, IEnumerable<Type> knownTypes)
        {
            object obj = null;

            if (!string.IsNullOrEmpty(json))
            {
                json = fromDateRegex.Replace(json, fromDateEvaluator);

                byte[] bytes = Encoding.UTF8.GetBytes(json);
                using (var ms = new MemoryStream(bytes))
                {
                    obj = Serializer.GetJsonSerializer(type, knownTypes).ReadObject(ms);
                }
            }

            return obj;
        }

        /// <summary>
        /// Converts a string of Json to the supplied object type.
        /// </summary>
        /// <typeparam name="T">The type to return.</typeparam>
        /// <param name="json">Json representing a .NET object.</param>
        /// <returns>An instance of <typeparamref name="T"/> or null if the value of json is null or empty.</returns>
        public static T ConvertFromJsonString<T>(string json)
        {
            return (T)Serializer.ConvertFromJsonString(json, typeof(T), Type.EmptyTypes);
        }

        /// <summary>
        /// Converts a string of Json to the supplied object type.
        /// </summary>
        /// <typeparam name="T">The type to return.</typeparam>
        /// <param name="json">Json representing a .NET object.</param>
        /// <param name="knownTypes">
        /// An <see cref="T:IEnumerable`1"/> of <see cref="T:System.Type"/> that contains 
        /// the types that may be present in the object graph.
        /// </param>
        /// <returns>An instance of <typeparamref name="T"/> or null if the value of json is null or empty.</returns>
        public static T ConvertFromJsonString<T>(string json, IEnumerable<Type> knownTypes)
        {
            return (T)Serializer.ConvertFromJsonString(json, typeof(T), knownTypes);
        }

        #endregion

        #region Xml Serialization

        /// <summary>
        /// Gets the cached <see cref="T:XmlSerializer"/>.
        /// </summary>
        /// <param name="type">A .NET object type.</param>
        /// <returns>An instance of <see cref="T:XmlSerializer"/></returns>
        public static XmlSerializer GetXmlSerializer(Type type)
        {
            return GetXmlSerializer(type, null);
        }

        /// <summary>
        /// Gets the cached <see cref="T:XmlSerializer"/>.
        /// </summary>
        /// <param name="type">A .NET object type.</param>
        /// <param name="extraTypes">
        /// An <see cref="T:IEnumerable`1"/> of <see cref="T:System.Type"/> that contains 
        /// the types that may be present in the object graph.
        /// </param>
        /// <returns>An instance of <see cref="T:XmlSerializer"/>.</returns>
        public static XmlSerializer GetXmlSerializer(Type type, IEnumerable<Type> extraTypes)
        {
            var key = new StringBuilder();
            if (extraTypes != null)
            {
                foreach (var t in extraTypes)
                {
                    key.AppendFormat("_{0}", t.GetHashCode());
                }
            }

            string hash = string.Format("{0}{1}", type.GetHashCode(), key);

            XmlSerializer serializer = null;
            if (xmlSerializers.TryGetValue(hash, out serializer))
            {
                return serializer;
            }
            else
            {
                xmlSerializers[hash] = extraTypes == null ? new XmlSerializer(type) : new XmlSerializer(type, extraTypes.ToArray());

                return xmlSerializers[hash];
            }
        }

        /// <summary>
        /// Converts a .NET object to a string of XML.
        /// </summary>
        /// <remarks>The object must be serializable.</remarks>
        /// <param name="obj">Object to convert.</param>
        /// <returns>A xml string represting the object parameter. The returned value will be null of the object is null.</returns>
        public static string ConvertToXmlString(object obj)
        {
            string xml = null;

            //if (obj != null)
            //{
            //    using (var sw = new StringWriter(CultureInfo.InvariantCulture))
            //    {
            //        var ser = GetXmlSerializer(obj.GetType());
            //        ser.Serialize(sw, obj);
            //        xml = sw.ToString();
            //    }
            //}

            if (obj != null)
            {
                using (var ms = new MemoryStream())
                {
                    var ser = GetXmlSerializer(obj.GetType());
                    using (var xtw = new XmlTextWriter(ms, Encoding.UTF8))
                    {
                        xtw.Formatting = Formatting.Indented;
                        ser.Serialize(xtw, obj);
                        ms.Seek(0, SeekOrigin.Begin);
                        using (var sr = new StreamReader(ms, Encoding.UTF8))
                        {
                            xml = sr.ReadToEnd();
                        }
                    }
                }
            }

            return xml;
        }

        /// <summary>
        /// Converts a string of xml to the supplied object type. 
        /// You will need to cast this object back to its expected type. 
        /// </summary>
        /// <remarks>The object must be serializable.</remarks>
        /// <param name="xml">Xml representing a .NET object.</param>
        /// <param name="type">The type of object which the xml represents.</param>
        /// <returns>An instance of object or null if the value of xml is null or empty.</returns>
        public static object ConvertFromXmlString(string xml, Type type)
        {
            object obj = null;

            //if (!string.IsNullOrEmpty(xml))
            //{
            //    using (var sr = new StringReader(xml))
            //    {
            //        obj = Serializer.GetXmlSerializer(type).Deserialize(sr);
            //    }
            //}

            if (!string.IsNullOrEmpty(xml))
            {
                byte[] bytes = Encoding.UTF8.GetBytes(xml);
                using (var ms = new MemoryStream(bytes))
                {
                    using (var xtw = new XmlTextWriter(ms, Encoding.UTF8))
                    {
                        obj = Serializer.GetXmlSerializer(type).Deserialize(ms);
                    }
                }
            }

            return obj;
        }

        /// <summary>
        /// Converts a string of xml to the supplied object type.
        /// </summary>
        /// <remarks>The object must be serializable.</remarks>
        /// <typeparam name="T">The type to return.</typeparam>
        /// <param name="xml">Xml representing a .NET object.</param>
        /// <returns>An instance of <typeparamref name="T"/> or null if the value of xml is null or empty.</returns>
        public static T ConvertFromXmlString<T>(string xml)
        {
            return (T)Serializer.ConvertFromXmlString(xml, typeof(T));
        }

        /// <summary>
        /// Converts a .NET object to a XML file.
        /// </summary>
        /// <remarks>The object must be serializable.</remarks>
        /// <param name="obj">Object to convert.</param>
        /// <param name="path">Location of the XML file.</param>
        public static void ConvertToXmlFile(object obj, string path)
        {
            if (obj != null)
            {
                using (var sw = new StreamWriter(path, false, Encoding.UTF8))
                {
                    Serializer.GetXmlSerializer(obj.GetType()).Serialize(sw, obj);
                }
            }
        }

        /// <summary>
        /// Converts a XML file from disk to a .NET object. You will need to cast this object back 
        /// to its expected type. 
        /// </summary>
        /// <remarks>The object must be serializable.</remarks>
        /// <param name="path">Location of the XML file.</param>
        /// <param name="type">The type of object which the file represents.</param>
        /// <returns>An instance of object.</returns>
        public static object ConvertFromXmlFile(string path, Type type)
        {
            object obj = null;

            //using (var sr = new StreamReader(path, Encoding.UTF8))
            //{
            //    obj = Serializer.GetXmlSerializer(type).Deserialize(sr);
            //}

            using (var sr = new StreamReader(path, Encoding.UTF8))
            {
                /*
                 * Note:
                 * Here we must create an instance of XmlTextReader and set it's property 
                 * Normalization to false. So that,we can have the same values as what had been serialized.
                 */
                using (var xtr = new XmlTextReader(sr))
                {
                    xtr.Normalization = false;
                    obj = Serializer.GetXmlSerializer(type).Deserialize(xtr);
                }
            }


            return obj;
        }

        /// <summary>
        /// Converts a XML file from disk to a .NET object.
        /// </summary>
        /// <remarks>The object must be serializable.</remarks>
        /// <typeparam name="T">The type to return.</typeparam>
        /// <param name="path">Location of the XML file.</param>
        /// <returns>An instance of <typeparamref name="T"/>.</returns>
        public static T ConvertFromXmlFile<T>(string path)
        {
            return (T)Serializer.ConvertFromXmlFile(path, typeof(T));
        }

        #endregion

        #region Binary Serialization

        /// <summary>
        /// Clones an object. You will need to cast this object back to its expected type. 
        /// before the clone happens, a check with Serializer.CanBinarySerialize will be made.
        /// </summary>
        /// <param name="obj">The object to clone.</param>
        /// <remarks>
        /// The object to be cloned must be serializable. 
        /// The default is to use the <see cref="T:System.Runtime.Serialization.Formatters.Binary.BinaryFormatter" />. 
        /// You may also choose to use the Microsoft .NET 3.0 <see cref="T:System.Runtime.Serialization.NetDataContractSerializer" /> 
        /// provided as part of WCF.
        /// </remarks>
        /// <returns>A new object. Null will be return if obj is null or CanBinarySerialize is false.</returns>
        public static object CloneObject(object obj)
        {
            object newObj = null;

            if (CanBinarySerialize && obj != null)
            {
                using (var buffer = new MemoryStream())
                {
                    var formatter = new BinaryFormatter();
                    formatter.Serialize(buffer, obj);
                    buffer.Position = 0;
                    newObj = formatter.Deserialize(buffer);
                }
            }

            return newObj;
        }

        /// <summary>
        /// Clones an object. before the clone happens, a check with Serializer.CanBinarySerialize will be made.
        /// </summary>
        /// <remarks>
        /// The T to be cloned must be serializable. 
        /// The default is to use the <see cref="T:System.Runtime.Serialization.Formatters.Binary.BinaryFormatter" />. 
        /// You may also choose to use the Microsoft .NET 3.0 <see cref="T:System.Runtime.Serialization.NetDataContractSerializer" /> 
        /// provided as part of WCF.
        /// </remarks>
        /// <typeparam name="T">The type of obj.</typeparam>
        /// <param name="obj">The object to clone.</param>
        /// <returns>A new <typeparamref name="T"/> object. Null will be return if obj is null or CanBinarySerialize is false.</returns>
        public static T CloneObject<T>(object obj)
        {
            return (T)CloneObject(obj);
        }

        /// <summary>
        /// Converts a .NET object to a byte array. before the conversion happens, a check with 
        /// Serializer.CanBinarySerialize will be made.
        /// </summary>
        /// <remarks>
        /// The default is to use the <see cref="T:System.Runtime.Serialization.Formatters.Binary.BinaryFormatter" />. 
        /// You may also choose to use the Microsoft .NET 3.0 <see cref="T:System.Runtime.Serialization.NetDataContractSerializer" /> 
        /// provided as part of WCF.
        /// </remarks>
        /// <param name="obj">Object to convert.</param>
        /// <returns>A byte arry representing the object paramter. Null will be return if obj is null or CanBinarySerialize is false.</returns>
        public static byte[] ConvertToByteArray(object obj)
        {
            byte[] bytes = null;

            if (CanBinarySerialize && obj != null)
            {
                using (var ms = new MemoryStream())
                {
                    new BinaryFormatter().Serialize(ms, obj);
                    ms.Position = 0;
                    bytes = new Byte[ms.Length];
                    ms.Read(bytes, 0, bytes.Length);
                }
            }

            return bytes;
        }

        /// <summary>
        /// Converts a byte array to a .NET object. You will need to cast this object back to its expected type. 
        /// If the array is null or empty, it will return null.
        /// </summary>
        /// <remarks>
        /// The default is to use the <see cref="T:System.Runtime.Serialization.Formatters.Binary.BinaryFormatter" />. 
        /// You may also choose to use the Microsoft .NET 3.0 <see cref="T:System.Runtime.Serialization.NetDataContractSerializer" /> 
        /// provided as part of WCF.
        /// </remarks>
        /// <param name="bytes">An array of bytes represeting a .NET object</param>
        /// <returns>The byte array converted to an object or null if the value of byteArray is null or empty.</returns>
        public static object ConvertFromByteArray(byte[] bytes)
        {
            object obj = null;

            if (CanBinarySerialize && bytes != null && bytes.Length > 0)
            {
                using (var ms = new MemoryStream())
                {
                    ms.Write(bytes, 0, bytes.Length);
                    ms.Position = 0;
                    //if (bytes.Length > 4)
                    //{
                    //    obj = formatter.Deserialize(ms);
                    //}
                    obj = new BinaryFormatter().Deserialize(ms);
                }
            }

            return obj;
        }

        /// <summary>
        /// Converts a byte array to a .NET object. If the array is null or empty, it will return null.
        /// </summary>
        /// <remarks>
        /// The default is to use the <see cref="T:System.Runtime.Serialization.Formatters.Binary.BinaryFormatter" />. 
        /// You may also choose to use the Microsoft .NET 3.0 <see cref="T:System.Runtime.Serialization.NetDataContractSerializer" /> 
        /// provided as part of WCF.
        /// </remarks>
        /// <typeparam name="T">The Type to return.</typeparam>
        /// <param name="bytes">An array of bytes represeting a .NET object</param>
        /// <returns>The byte array converted to a <typeparamref name="T"/> or null if the value of byteArray is null or empty.</returns>
        public static T ConvertFromByteArray<T>(byte[] bytes)
        {
            return (T)Serializer.ConvertFromByteArray(bytes);
        }


        /// <summary>
        /// Saves an object to disk as a binary file.
        /// </summary>
        /// <remarks>
        /// The default is to use the <see cref="System.Runtime.Serialization.Formatters.Binary.BinaryFormatter" />. 
        /// You may also choose to use the Microsoft .NET 3.0 <see cref="System.Runtime.Serialization.NetDataContractSerializer" /> 
        /// provided as part of WCF.
        /// </remarks>
        /// <param name="obj">Object to Save.</param>
        /// <param name="path">Location of the binary file.</param>
        public static void ConvertToBinaryFile(object obj, string path)
        {
            if (CanBinarySerialize && obj != null)
            {
                byte[] bytes = Serializer.ConvertToByteArray(obj);
                if (bytes != null)
                {
                    using (var fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))
                    {
                        using (var bw = new BinaryWriter(fs))
                        {
                            bw.Write(bytes);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Converts a binary file from disk to a .NET object. You will need to cast this object back to its expected type. 
        /// If the binary file is not exist or empty, it will return null.
        /// </summary>
        /// <remarks>
        /// The default is to use the <see cref="System.Runtime.Serialization.Formatters.Binary.BinaryFormatter" />. 
        /// You may also choose to use the Microsoft .NET 3.0 <see cref="System.Runtime.Serialization.NetDataContractSerializer" /> 
        /// provided as part of WCF.
        /// </remarks>
        /// <param name="path">Location of the binary file.</param>
        /// <returns>An instance of object.</returns>
        public static object ConvertFromBinaryFile(string path)
        {
            using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
            {
                using (var br = new BinaryReader(fs))
                {
                    byte[] bytes = new byte[fs.Length];
                    br.Read(bytes, 0, (int)fs.Length);
                    return Serializer.ConvertFromByteArray(bytes);
                }
            }
        }

        /// <summary>
        /// Converts a binary file from disk to a .NET object. If the binary file is not exist or empty, it will return null.
        /// </summary>
        /// <remarks>
        /// The default is to use the <see cref="System.Runtime.Serialization.Formatters.Binary.BinaryFormatter" />. 
        /// You may also choose to use the Microsoft .NET 3.0 <see cref="System.Runtime.Serialization.NetDataContractSerializer" /> 
        /// provided as part of WCF.
        /// </remarks>
        /// <typeparam name="T">The Type to return.</typeparam>
        /// <param name="path">Location of the binary file.</param>
        /// <returns>An instance of <typeparamref name="T"/>.</returns>
        public static T ConvertFromBinaryFile<T>(string path)
        {
            return (T)Serializer.ConvertFromBinaryFile(path);
        }

        #endregion

        #region Collection Serialization

        /// <summary>
        /// Creates a NameValueCollection from two string.
        /// The first contains the key pattern and the second contains the values spaced according to the keys.
        /// </summary>
        /// <param name="keys">Keys for the namevalue collection.</param>
        /// <param name="values">Values for the namevalue collection.</param>
        /// <returns>A NVC populated based on the keys and vaules.</returns>
        /// <example>
        /// string keys = "key1:S:0:3:key2:S:3:2:";
        /// string values = "12345";
        /// This would result in a NameValueCollection with two keys (Key1 and Key2) with the values 123 and 45
        /// </example>
        public static NameValueCollection ConvertToNameValueCollection(string keys, string values)
        {
            var nvc = new NameValueCollection();

            if (keys != null && values != null && keys.Length > 0 && values.Length > 0)
            {
                char[] splitter = new char[1] { ':' };
                string[] keyNames = keys.Split(splitter);

                for (int i = 0; i < (keyNames.Length / 4); i++)
                {
                    int start = int.Parse(keyNames[(i * 4) + 2], CultureInfo.InvariantCulture);
                    int len = int.Parse(keyNames[(i * 4) + 3], CultureInfo.InvariantCulture);
                    string key = keyNames[i * 4];

                    //Future version will support more complex types
                    if (((keyNames[(i * 4) + 1] == "S") && (start >= 0)) && (len > 0) && (values.Length >= (start + len)))
                    {
                        nvc[key] = values.Substring(start, len);
                    }
                }
            }

            return nvc;
        }

        /// <summary>
        /// Creates a the keys and values strings for the simple serialization based on a NameValueCollection.
        /// </summary>
        /// <param name="nvc">NameValueCollection to convert.</param>
        /// <param name="keys">the ref string will contain the keys based on the key format.</param>
        /// <param name="values">the ref string will contain all the values of the namevaluecollection.</param>
        public static void ConvertFromNameValueCollection(NameValueCollection nvc, ref string keys, ref string values)
        {
            if (nvc == null || nvc.Count == 0)
                return;

            var sbKey = new StringBuilder();
            var sbValue = new StringBuilder();

            int index = 0;
            foreach (var key in nvc.AllKeys)
            {
                if (key.IndexOf(':') != -1)
                    throw new ArgumentException("ExtendedAttributes Key can not contain the character \":\"");

                var v = nvc[key];
                if (!string.IsNullOrEmpty(v))
                {
                    sbKey.AppendFormat("{0}:S:{1}:{2}:", key, index, v.Length);
                    sbValue.Append(v);
                    index += v.Length;
                }
            }
            keys = sbKey.ToString();
            values = sbValue.ToString();
        }

        #endregion

        #endregion
    }
}
